/********************************************************************
	Author:		Kanrong Shan
*********************************************************************/
#pragma once

#include <Unknwn.h>
#include "ComPtr.h"

#if _MSC_VER >= 1600
#include <type_traits>
#include <exception>
#endif


template <class T>
class ComObject : public T
{
public:
	template <class I>
	static HRESULT Create(__out I** object)
	{
		#if _MSC_VER >= 1600
		static_assert(std::tr1::is_base_of<I, T>::value, "Template arg T must derive from I");
		#endif

		ComPtr<I> comPtr;
		try
		{
			comPtr = new ComObject<T>();
		}
		catch(std::bad_alloc)
		{
			return E_FAIL;
		}

		if( (NULL == object) || (*object !=NULL) )
			return E_POINTER;

		(*object) = comPtr;
		if(NULL != (*object))
			(*object)->AddRef();

		return S_OK;
	}

	template <class I, class TArg1>
#if _MSC_VER >= 1600
	static HRESULT Create(TArg1 &&arg1, __out I** object)
#else
	static HRESULT Create(TArg1 &arg1, __out I** object)
#endif
	{
		#if _MSC_VER >= 1600
		static_assert(std::tr1::is_base_of<I, T>::value, "Template arg T must derive from I");
		#endif

		// Results an export of others functions in a library	
		//http://connect.microsoft.com/VisualStudio/feedback/details/259564/using-std-nothrow-and-md-results-in-an-export-of-std-init-locks-operator
		//ComPtr<I> comPtr = new (std::nothrow) ComObject<T>(arg1);	
		//if (!comPtr)
		//{
		//	return E_FAIL;
		//}
		ComPtr<I> comPtr;
		try
		{
			comPtr = new ComObject<T>(arg1);
		}
		catch(std::bad_alloc)
		{
			return E_FAIL;
		}


		if( (NULL == object) || (*object !=NULL) )
			return E_POINTER;

		(*object) = comPtr;
		if(NULL != (*object))
			(*object)->AddRef();

		return S_OK;
	}

	template <class I, class TArg1, class TArg2>
	static HRESULT Create(TArg1 &arg1, TArg2 &arg2, __out I** object)
	{
		ComPtr<I> comPtr;
		try
		{
			comPtr = new ComObject<T>(arg1, arg2);
		}
		catch(std::bad_alloc)
		{
			return E_FAIL;
		}


		if( (NULL == object) || (*object !=NULL) )
			return E_POINTER;

		(*object) = comPtr;
		if(NULL != (*object))
			(*object)->AddRef();

		return S_OK;
	}

private:
	ComObject(): T(), m_refCount(0)
	{
	}

	template <class TArg1>
#if _MSC_VER >= 1600
	ComObject(TArg1 &&arg1)
		: T(arg1), m_refCount(0)
#else
	ComObject(TArg1 &arg1)
		: T(arg1), m_refCount(0)
#endif
	{
	}

	template <class TArg1, class TArg2>
#if _MSC_VER >= 1600
	ComObject(TArg1 &&arg1, TArg2 &&arg2)
		: T(arg1, arg2), m_refCount(0)
#else
	ComObject(TArg1 &arg1, TArg2 &arg2)
		: T(arg1, arg2), m_refCount(0)
#endif
	{
	}

	HRESULT __stdcall QueryInterface(const IID &iid, void **object)
	{
		if (NULL == object)
		{
			return E_POINTER;
		}

		*object = NULL;

		if (T::QueryInterfaceHelper(iid, object))
		{
			static_cast<IUnknown*>(*object)->AddRef();
			return S_OK;
		}
		return E_NOINTERFACE;
	}

	unsigned long __stdcall AddRef()
	{
		return static_cast<unsigned long>(
			InterlockedIncrement(reinterpret_cast<long*>(&m_refCount)));
	}

	unsigned long __stdcall Release()
	{
		unsigned long refCount = static_cast<unsigned long>(
			InterlockedDecrement(reinterpret_cast<long*>(&m_refCount)));

		if (refCount == 0)
		{
			delete this;
		}

		return refCount;
	}

private:
	// The variable holding the ref count
	unsigned long m_refCount;
};


template <typename I>
struct CastHelper
{
	template <typename T>
	static bool CastTo(const IID &iid, T* objPtr, void **interfacePtr)
	{
		if (__uuidof(I) == iid || IID_IUnknown == iid)
		{
			*interfacePtr = static_cast<I*>(objPtr);
			return true;
		}
		return false;
	}
};